package conversion.utils.converter;

import java.util.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import com.fasterxml.jackson.core.JsonProcessingException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.opencsv.CSVWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;


/**
 * XmlProcessor 类用于处理XML字符串，提供转换、格式化和缩进等操作。
 * @author ZJW
 * 版本：1.0
 */
public class XmlProcessor {


    /**
     * 验证指定的xml字符串格式是否正确
     *
     * @param xml 要验证xml字符串
     * @return 如果正确则返回ture，否则返回false
     */
    public static boolean isFormatValid(String xml) {
        try {
            DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document document = documentBuilder.parse(new InputSource(new StringReader(xml)));
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 从XML字符串中删除节点之间的空格、缩进和换行符，但保留节点内容中的空格和缩进。
     *
     * @param xmlString 包含XML内容的字符串
     * @return 清除节点之间空白字符后的XML字符串
     */
    public static String removeIndentationAndNewlines(String xmlString) {
        // 删除节点之间的空格、缩进和换行符，但保留节点内容中的空格和缩进
        String cleanedXml = xmlString.replaceAll(">\\s+<", "><");

        return cleanedXml;
    }


    /**
     * 将XML字符串转换为CSV格式的字符串
     *
     * @param xmlString XML字符串
     * @return CSV格式的字符串
     * @throws Exception 解析XML异常
     */
    public static String convertToCSV(String xmlString) throws Exception {

        // 将XML字符串解析为文档对象
        Document document = transformXMLStringToDocument(xmlString);

        Element rootElement = document.getDocumentElement();

        // 获取元素节点的所有子元素节点的名称，作为CSV文件的一行数据的列名
        Element element1 = (Element) rootElement.getChildNodes().item(1);
        NodeList childNodes1 = element1.getChildNodes();
        List<String> headerRowData = new ArrayList<>();
        for (int i = 0; i < childNodes1.getLength(); i++) {
            Node childNode = childNodes1.item(i);
            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

                headerRowData.add(childNode.getNodeName());
            }
        }

        // 存储数据的列表
        List<List<String>> dataList = new ArrayList<>();
        dataList.add(headerRowData); // 添加列名作为CSV文件第一行数据
        // 遍历XML文档子元素节点，获取存储CSV文件每一行数据的列表
        NodeList nodeList = rootElement.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            // 判断当前节点是否为元素节点
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                // 存储一行数据的列表
                List<String> rowData = new ArrayList<>();
                NodeList childNodes = element.getChildNodes();
                for (int j = 0; j < childNodes.getLength(); j++) {
                    Node childNode = childNodes.item(j);
                    // 判断当前子节点是否为元素节点，如果是就获取其文本内容并添加到存储一行数据的列表中
                    if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                        rowData.add(childNode.getTextContent());
                    }
                }
                // 将存储一行数据的列表添加到存储数据的列表中
                dataList.add(rowData);
            }
        }

        // 将存储数据的列表转换为CSV格式的字符串
        StringWriter stringWriter = new StringWriter();
        CSVWriter csvWriter = new CSVWriter(stringWriter);
        for (List<String> rowData : dataList) {
            csvWriter.writeNext(rowData.toArray(new String[0]));
        }
        csvWriter.close();
        String csvString = stringWriter.toString();
        stringWriter.close();
        return csvString;

    }


    /**
     *  将指定的xml字符串转换成json字符串
     *
     * @param xmlString xml字符串
     * @return json字符串
     */
    public static String convertToJSON(String xmlString) throws JsonProcessingException {
        // 创建Jackson的XML解析器对象
        XmlMapper xmlMapper = new XmlMapper();
        // 创建Jackson的JSON解析器对象
        ObjectMapper objectMapper = new ObjectMapper();

        // 解析XML字符串，并将其转换成JsonNode对象
        JsonNode jsonNode = xmlMapper.readTree(xmlString);

        // 将JsonNode对象转换成JSON格式，并返回JSON字符串
        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);

    }


    /**
     * 将指定xml字符串转换成yaml字符串
     *
     * @param xmlString 要转换的xml字符串
     * @return 转换成功的yaml字符串
     */
    public static String convertToYAMl(String xmlString) throws ParserConfigurationException, IOException, SAXException {

        // 创建DocumentBuilder对象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 解析XML字符串
        Document document = builder.parse(new InputSource(new StringReader(xmlString)));

        // 设置DumperOptions参数和默认值
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);

        // 创建Yaml对象，并设置DumperOptions
        Yaml yaml = new Yaml(options);

        // 将XML节点转换为Map，并使用Yaml对象将其转换为YAML格式并返回
        return yaml.dump(getNodeAsMap(document.getDocumentElement()));

    }

    /**
     * 将XML文件中的节点转换为Map对象
     *
     * @param node XML文件中的节点
     * @return Map对象
     */
    private static Object getNodeAsMap(Node node) {
        Map<String, Object> map = new LinkedHashMap<>(); // 创建一个LinkedHashMap对象
        NamedNodeMap attributes = node.getAttributes(); // 获取节点的属性列表

        if (attributes.getLength() > 0) {
            Map<String, Object> attributeMap = new LinkedHashMap<>(); // 创建一个LinkedHashMap对象
            for (int i = 0; i < attributes.getLength(); i++) { // 遍历属性列表
                attributeMap.put(attributes.item(i).getNodeName(), attributes.item(i).getNodeValue()); // 将属性名称和属性值添加到attributeMap中
            }
            map.put("_attributes", attributeMap); // 将attributeMap添加到map中，使用"_attributes"作为key
        }

        if (node.hasChildNodes()) { // 如果节点有子节点
            NodeList nodeList = node.getChildNodes(); // 获取子节点列表

            if (nodeList.getLength() == 1 && nodeList.item(0).getNodeType() == Node.TEXT_NODE) {
                return nodeList.item(0).getNodeValue(); // 如果子节点列表只有一个文本节点，返回该节点的值
            }

            Map<String, Object> childrenMap = new LinkedHashMap<>(); // 创建一个LinkedHashMap对象，用于存储子节点

            for (int i = 0; i < nodeList.getLength(); i++) { // 遍历子节点列表
                Node childNode = nodeList.item(i);

                if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 如果子节点是元素节点
                    if (childrenMap.containsKey(childNode.getNodeName())) { // 如果子节点已经出现在子节点列表中
                        Object childValue = childrenMap.get(childNode.getNodeName()); // 获取该子节点的值

                        if (childValue instanceof List) { // 如果该子节点的值是一个List对象
                            ((List<Object>) childValue).add(getNodeAsMap(childNode)); // 将该子节点添加到List中
                        } else { // 如果该子节点的值不是一个List对象
                            List<Object> childList = new ArrayList<>(); // 创建一个ArrayList对象
                            childList.add(childValue); // 将该子节点的值添加到List中
                            childList.add(getNodeAsMap(childNode)); // 将该子节点添加到List中
                            childrenMap.put(childNode.getNodeName(), childList); // 将新的List对象作为该子节点的值添加到childrenMap中
                        }
                    } else { // 如果子节点没有出现在子节点列表中
                        childrenMap.put(childNode.getNodeName(), getNodeAsMap(childNode)); // 将该子节点添加到childrenMap中
                    }
                }
            }

            if (attributes.getLength() == 0 && childrenMap.size() == 1) {
                return childrenMap.values().iterator().next(); // 如果该节点只有一个子节点，返回该子节点的值
            }

            if (attributes.getLength() == 0) {
                return childrenMap; // 如果该节点没有属性，返回childrenMap
            }

            map.putAll(childrenMap); // 将childrenMap中的内容添加到map中
        }

        return map; // 返回map对象
    }


    /**
     * 处理节点名称，使其符合XML命名规则
     *
     * @param nodeName 原始节点名称
     * @return 处理后的节点名称
     */
    public static String processNodeName(String nodeName) {
        // 删除非法字符
        nodeName = nodeName.replaceAll("[^\\w.-:]", "");

        // 替换连字符 "-" 或点号 "." 开头的情况
        if (nodeName.startsWith("-") || nodeName.startsWith(".")) {
            nodeName = "_" + nodeName.substring(1);
        }

        // 添加前缀，防止使用保留的XML关键字作为节点名称
        if (isXMLKeyword(nodeName)) {
            nodeName = "prefix_" + nodeName;
        }

        return nodeName;
    }

    /**
     * 判断节点名称是否是保留的XML关键字
     *
     * @param nodeName 节点名称
     * @return 是否是XML关键字
     */
    public static boolean isXMLKeyword(String nodeName) {
        String[] xmlKeywords = {"xml", "xmlns"};
        for (String keyword : xmlKeywords) {
            if (keyword.equalsIgnoreCase(nodeName)) {
                return true;
            }
        }
        return false;
    }



    /**
     * 美化XML格式的字符串
     *
     * @param xmlString XML格式的字符串
     * @return 美化后的XML格式的字符串
     * @throws Exception 格式化过程中发生的异常
     */
    public static String formatXML(String xmlString) {
        try {

            Document document = transformXMLStringToDocument(xmlString);

            // 创建 Transformer 对象并设置输出参数
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

            // 格式化 XML 并将结果写入字符串缓冲区
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(document), new StreamResult(writer));
            return writer.toString();

        } catch (Exception e) {
            System.err.println("格式化 XML 字符串时发生错误：" + e.getMessage());
            return null;
        }
    }

    /**
     * 将XML格式的字符串转换为Document对象
     *
     * @param xmlString XML格式的字符串
     * @return Document对象
     * @throws Exception 转换过程中发生的异常
     */
    public static Document transformXMLStringToDocument(String xmlString) throws Exception {
        // 创建一个文档工厂实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 创建一个文档构建器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 解析XML字符串并返回Document对象
        return builder.parse(new InputSource(new StringReader(xmlString)));
    }
}